import { Vec2, sys, native, view } from "cc";
import { oops } from "db://oops-framework/core/Oops";
import { smc } from "../game/common/SingletonModuleComp";

export function hasOwnProperty<T>(obj: T, key: PropertyKey): key is keyof T {
    return Object.prototype.hasOwnProperty.call(obj, key);
}

export function getSafeProperty<T, K extends keyof T>(obj: T, key: K): T[K] | undefined;
export function getSafeProperty<T>(obj: T, key: string): unknown | undefined;
export function getSafeProperty(obj: any, key: string) {
    return obj != null && Object.prototype.hasOwnProperty.call(obj, key)
        ? obj[key]
        : undefined;
}

// 1. 定义通用类型
type TypeMap = Record<string, any>; // 基础映射类型

// 2. 创建通用getType函数
export function getType<T extends TypeMap, K extends keyof T>(
    typeMap: T, // 类型映射对象
    key: K      // 必须是typeMap的键
): T[K] {     // 返回对应键的值类型
    return typeMap[key];
}

/**
 * 根据PS框出的区域，计算相机景深的偏移量
 * @param displayRegion 显示区域
 * @param screenSize 屏幕尺寸
 * @param mapHeight 设计图高度
 * @param isPs 是否是PS中的坐标
 * @returns 
 */
export function calculateCameraSettings(
    displayRegion: { x: number, y: number, width: number, height: number },
    screenSize: { width: number, height: number },
    mapHeight: number,
    isPs = true
): { orthoHeight: number, offset: Vec2 } {

    let y = isPs ? (mapHeight - displayRegion.y - displayRegion.height) : displayRegion.y;

    // 按高度适配
    let orthoHeight = displayRegion.height / 2;
    // // 按宽度适配
    // let scale = screenSize.width / displayRegion.width;
    // // orthoHeight是高度的一半
    // orthoHeight = screenSize.height / scale / 2;

    let offsetX = displayRegion.x + (displayRegion.width - screenSize.width) / 2;
    let offsetY = y + (displayRegion.height - screenSize.height) / 2;

    offsetX += screenSize.width / 2;
    offsetY += screenSize.height / 2;

    return {
        orthoHeight,
        offset: new Vec2(offsetX, offsetY)
    };
}

export function waitJs(ms: number) {
    return new Promise((resolve, reject) => {
        oops.timer.scheduleOnce(() => {
            resolve(1);
        }, ms);
    });
}
/**
 * 改变屏幕方向
 * @param customEventData "1": 竖屏 2：横屏
 */
export function appLockScreen(customEventData: string) {
    console.log("修改屏幕方向", customEventData)
    // oops.message.dispatchEvent("set-screen-orientation", customEventData);
    // if (sys.os === sys.OS.ANDROID && sys.isNative) {
    //     // 调用 Android 原生方法
    //     native.reflection.callStaticMethod(
    //         "com/cocos/game/NativeBridge",
    //         "lockScreenLandscape",
    //         "(Ljava/lang/String;)V",
    //         customEventData
    //     );
    // }
    if (sys.isNative) {
        sendToNative("lockScreenLandscape", customEventData);
    }
}
// 字节转换函数（自动选择最佳单位）
// console.log(formatBytes(500));       // 输出: "0.49 KB"
// console.log(formatBytes(1024));      // 输出: "1 KB"
// console.log(formatBytes(1536000));   // 输出: "1.46 MB"
// console.log(formatBytes(3072000000)); // 输出: "2.86 GB"
// console.log(formatBytes(0));         // 输出: "0 KB"
export function formatBytes(bytes: number) {
    if (bytes === 0) return '0 KB';

    const kb = 1024;
    const mb = kb * 1024;
    const gb = mb * 1024;

    // 保留2位小数
    const format = (num: number) => num.toFixed(2).replace(/\.?0+$/, '');

    if (bytes >= gb) {
        return format(bytes / gb) + ' GB';
    } else if (bytes >= mb) {
        return format(bytes / mb) + ' MB';
    } else {
        // 最小显示单位为KB，即使小于1KB也显示为0.XX KB
        return format(Math.max(bytes / kb, 0.01)) + ' KB';
    }
}

export function getWebParam(str1: string) {
    let str2 = str1.split("&");
    let obj: any = {};
    str2.forEach(val => {
        let a = val.split("=");
        obj[a[0]] = a[1];
    })
    return obj;
}

export function getDistance(touch1: any, touch2: any): number {
    const dx = touch1.x - touch2.x;
    const dy = touch1.y - touch2.y;
    return Math.sqrt(dx * dx + dy * dy);
}

export function sendToNative(funName: string, arg: string) {
    native.jsbBridgeWrapper.dispatchEventToNative("JSToIos", funName + "###!!!" + arg);
}

export function getFileNameForUrl(url: string) {
    // 匹配最后一个斜杠和问号之间的内容
    const match = url.match(/\/([^\/?]+)(?:\?|$)/);
    return match ? match[1] : null;
}

export function guid() {
    let guid: string = "";
    for (let i = 1; i <= 32; i++) {
        let n = Math.floor(Math.random() * 16.0).toString(16);
        guid += n;
        if ((i == 8) || (i == 12) || (i == 16) || (i == 20))
            guid += "-";
    }
    return guid;
}

// 预生成字符集（字母+数字）
const RANDOM_CHARS = (() => {
    const alpha = Array.from({ length: 26 }, (_, i) =>
        String.fromCharCode(65 + i, 97 + i)).join('');
    const nums = '0123456789';
    return alpha + nums;
})();

export function getRandomString(length: number, isNumber = false): string {
    let CHARS_KU = RANDOM_CHARS;
    if (isNumber) CHARS_KU = '0123456789';
    let result = CHARS_KU.charAt(Math.floor(Math.random() * 52)); // 首字母

    for (let i = 1; i < length; i++) {
        result += CHARS_KU.charAt(Math.floor(Math.random() * CHARS_KU.length));
    }

    return result;
}

export function getMimeTypeFromFilename(filenameOrExt: string): string {
    // 提取扩展名（兼容带点或不带点）
    let extension = filenameOrExt.split('.').pop()?.toLowerCase() || '';
    if (!extension) return 'application/octet-stream';

    // 完整映射表
    const mimeMap: Record<string, string> = {
        // 图片
        'png': 'image/png',
        'jpg': 'image/jpeg',
        'jpeg': 'image/jpeg',
        'gif': 'image/gif',
        'webp': 'image/webp',
        'svg': 'image/svg+xml',
        'bmp': 'image/bmp',
        'tiff': 'image/tiff',

        // 其他常见类型
        'json': 'application/json',
        'pdf': 'application/pdf',
        // 可继续扩展...
    };

    return mimeMap[extension] || 'application/octet-stream';
}

/**
 * 判断两个矩形是否相交（重叠部分面积 > 0）
 * @param rect1 - 矩形1: [左下x, 左下y, 右上x, 右上y]
 * @param rect2 - 矩形2: [左下x, 左下y, 右上x, 右上y]
 * @returns 是否相交
 */
function isRectangleOverlap(
    rect1: [number, number, number, number],
    rect2: [number, number, number, number]
): boolean {
    // 解构矩形坐标
    const [x1, y1, x2, y2] = rect1;
    const [x3, y3, x4, y4] = rect2;

    // 检查投影轴是否重叠[1,6,7](@ref)
    const xOverlap = x1 < x4 && x3 < x2; // X轴投影有重叠
    const yOverlap = y1 < y4 && y3 < y2; // Y轴投影有重叠

    return xOverlap && yOverlap;
}

/**
 * @description 节流函数 节流实用于限度间断触发事件的执行频率。
 * @param func 要进行节流的指标函数
 * @param delay 节流的工夫距离（毫秒）
 * @returns 返回一个通过节流解决的函数
 */
export function throttle<T extends (...args: any[]) => any>(
    fn: T,
    delay: number
): T {
    let lastCall: number | null = null; // 上一次调用的工夫戳
    return function (
        this: ThisParameterType<T>,
        ...args: Parameters<T>
    ): ReturnType<T> | void {
        const now = Date.now(); //以后工夫戳
        // 如果之前没有调用过,或者工夫距离超过了设定的值,执行指标函数
        if (!lastCall || now - lastCall >= delay) {
            lastCall = now;
            return fn.apply(this, args);
        }
    } as T;
}

/**
 * @description Cocos防抖函数 - 通过 oops.timer 单例实现
 * @param fn 需要防抖处理的函数
 * @param wait 延迟时间（毫秒）
 * @param immediate 是否立即执行（默认 false）
 * @returns 返回防抖处理后的函数（含 cancel 方法）
 */
export function cocosDebounce<T extends (...args: any[]) => any>(
    fn: T,
    wait: number = 50,
    immediate: boolean = false
): T & { cancel(): void } {
    // 保存定时器回调引用（用于取消）
    let scheduledCallback: (() => void) | null = null;
    // 保存最近一次调用的参数
    let lastArgs: any[] | null = null;

    const debounced = function (this: any, ...args: any[]) {
        lastArgs = args;

        // 取消之前的定时任务
        if (scheduledCallback) {
            oops.timer.unschedule(scheduledCallback);
            scheduledCallback = null;
        }

        // 立即执行模式处理
        if (immediate && !scheduledCallback) {
            fn.apply(this, lastArgs);
        }

        // 创建新的延迟回调（适配 Cocos 的时间单位转换）
        const executeDelayed = () => {
            if (!immediate) {
                fn.apply(this, lastArgs!);
            }
            scheduledCallback = null;
        };

        // 转换毫秒为秒（Cocos 定时器使用秒为单位）
        const delayInSeconds = wait / 1000;
        oops.timer.scheduleOnce(executeDelayed, delayInSeconds);
        scheduledCallback = executeDelayed;
    };

    // 添加取消方法
    debounced.cancel = function () {
        if (scheduledCallback) {
            oops.timer.unschedule(scheduledCallback);
            scheduledCallback = null;
        }
    };

    return debounced as T & { cancel(): void };
}

/**
 * @description 防抖函数 防抖用于确保一个函数在指定时间内只触发一次。它在短时间内屡次触发同一个事件时，会勾销之前的触发，直到最初一次触发后的肯定工夫距离内没有新的触发才执行函数。
 * @param {Function} fn
 * @param {number} delay
 * @param {boolean} immediate
 * @returns {Function}
 */
export function debounce<T extends (...args: any[]) => any>(
    fn: T,
    delay: number,
    immediate: boolean = false
): T & { cancel(): void } {
    let timerId: ReturnType<typeof setTimeout> | null = null; // 存储定时器
    // 定义一个cancel办法,用于勾销防抖
    const cancel = (): void => {
        if (timerId) {
            clearTimeout(timerId);
            timerId = null;
        }
    };
    const debounced = function (
        this: ThisParameterType<T>,
        ...args: Parameters<T>
    ): void {
        console.log('debounceddebounced')
        const context = this;
        if (timerId) {
            cancel();
        }
        if (immediate) {
            // 如果 immediate 为 true 并且没有正在期待执行的定时器，立刻执行指标函数
            if (!timerId) {
                fn.apply(context, args);
            }
            // 设置定时器，在延迟时间后将 timeoutId 设为 null
            timerId = setTimeout(() => {
                timerId = null;
            }, delay);
        } else {
            // 设置定时器，在延迟时间后执行指标函数
            timerId = setTimeout(() => {
                fn.apply(context, args);
            }, delay);
        }
    };
    // 将 cancel 办法附加到 debounced 函数上
    (debounced as any).cancel = cancel;
    return debounced as T & { cancel(): void };
}
function fallbackCopy(text: string) {
    const textarea = document.createElement("textarea");
    textarea.value = text;
    textarea.style.position = "fixed";
    textarea.style.opacity = "0";
    document.body.appendChild(textarea);
    textarea.select();
    textarea.setSelectionRange(0, 99999);

    try {
        const success = document.execCommand("copy");
        return success ? "1" : "0";
    } catch (err) {
        return "0";
    } finally {
        document.body.removeChild(textarea);
    }
}
// H5复制
export function copyText(text: string) {
    return new Promise((resolve, reject) => {
        if (sys.isNative) {
            native.copyTextToClipboard(text);
            resolve("1");
        }

        // 现代浏览器方案
        if (navigator.clipboard?.writeText) {
            navigator.clipboard.writeText(text)
                .then(() => {
                    resolve("1");
                })
                .catch(() => {
                    resolve(fallbackCopy(text));
                });
        } else {
            resolve(fallbackCopy(text));
        }
    })

}

/**
 * 对Date的扩展，将 Date 转化为指定格式的String
 * 月(Y)、月(m)、日(d)、小时(H)、分(M)、秒(S) 可以用 1-2 个占位符，
 * 例子：
 * dateFormat('YYYY-mm-dd HH:MM:SS', new Date()) ==> 2020-01-01 08:00:00
 * dateFormat('YYYY-mm-dd HH:MM:SS', 1754929219) ==> 2020-01-01 08:00:00
 * dateFormat('YYYY-mm-dd HH:MM:SS') ==> 2020-01-01 08:00:00
 */
export function dateFormat(fmt: string, indate?: any) {
    let date = new Date();
    if (typeof (indate) != 'object' && indate) {
        // @ts-ignore
        if (indate.length == 10) date = new Date(Number(indate) * 1000);
        // @ts-ignore
        if (indate.length == 13) date = new Date(Number(indate));
    }
    const opt = {
        "Y+": date.getFullYear().toString(), // 年
        "m+": (date.getMonth() + 1).toString(), // 月
        "d+": date.getDate().toString(), // 日
        "H+": date.getHours().toString(), // 时
        "M+": date.getMinutes().toString(), // 分
        "S+": date.getSeconds().toString() // 秒
        // 有其他格式化字符需求可以继续添加，必须转化成字符串
    };
    let ret
    for (let k in opt) {
        ret = new RegExp("(" + k + ")").exec(fmt)
        if (ret) {
            // @ts-ignore
            fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
        };
    };
    return fmt
}

/**
 * 计算图片在固定盒子内的显示尺寸（长边完全显示）
 * @param {number} boxWidth - 容器宽度
 * @param {number} boxHeight - 容器高度
 * @param {number} imgWidth - 图片原始宽度
 * @param {number} imgHeight - 图片原始高度
 * @returns {{ width: number, height: number }} 缩放后的图片尺寸
 */
export function calculateContainSize(boxWidth: number, boxHeight: number, imgWidth: number, imgHeight: number) {
    // 1. 计算容器与图片的宽高比
    const containerRatio = boxWidth / boxHeight;
    const imageRatio = imgWidth / imgHeight;

    let targetWidth, targetHeight;

    // 2. 核心比例计算逻辑
    if (containerRatio > imageRatio) {
        // 容器较宽：按高度缩放
        targetHeight = boxHeight;
        targetWidth = boxHeight * imageRatio;
    } else {
        // 容器较高：按宽度缩放
        targetWidth = boxWidth;
        targetHeight = boxWidth / imageRatio;
    }

    // 3. 边界情况处理（零尺寸/极小值）
    if (imgWidth <= 0 || imgHeight <= 0) {
        return { width: 0, height: 0 };
    }

    return {
        width: targetWidth,
        height: targetHeight
    };
}

export function setPicUrl(url: string) {
    if (oops.storage.get("pic-type") == "1") {
        return url + "?t=" + Date.now();
    }
    if (sys.isBrowser) {
        return "http://testupdate.gxyj17173.cn/800.png"
    }
    return url;
}

export function setOneKey(_data: any, name: string, value: any) {
    let nameArr = name.split('.');
    let len = nameArr.length;
    if (len >= 2) {
        let obj = _data[nameArr[0]];
        for (let i = 1; i < len - 1; i++) {
            obj = obj[nameArr[i]];
        }
        obj[nameArr[len - 1]] = value;
    } else {
        // 单层级变量，在state就是一个普通变量的情况
        _data[name] = value;
    }
}

export function getOneKey(_data: any, name: string) {
    let nameArr = name.split('.');
    let len = nameArr.length;
    if (len >= 2) {
        let obj = _data[nameArr[0]];
        for (let i = 1; i < len - 1; i++) {
            obj = obj[nameArr[i]];
        }
        return obj[nameArr[len - 1]];
    } else {
        // 单层级变量，在state就是一个普通变量的情况
        return _data[name];
    }
}

export function watchObject<T extends object>(obj: T, onChange: (key: keyof T, oldValue: T[keyof T], newValue: T[keyof T]) => void): T {
    return new Proxy(obj, {
        //@ts-ignore
        set(target, key: keyof T, newValue) {
            if (target[key] !== newValue) {
                const oldValue = target[key];
                target[key] = newValue;
                onChange(key, oldValue, newValue);
            }
            return true;
        }
    });
}

let _DesignSize = view.getDesignResolutionSize();
let _viewSize = view.getVisibleSize();
export function getPxDes(px: number = 1) {
    return _viewSize.width / _DesignSize.width * px;
}

/**
 * 生成指定范围内的随机整数
 * @param min 最小值（包含）
 * @param max 最大值（包含）
 * @returns 范围内的随机整数
 */
export function mtRand(min: number, max: number): number {
    // 确保min和max为整数
    const floorMin = Math.floor(min);
    const floorMax = Math.floor(max);
    
    // 确保min不大于max
    if (floorMin > floorMax) {
        throw new Error('min不能大于max');
    }
    
    // 计算范围大小
    const range = floorMax - floorMin + 1;
    
    // 生成[0, range)之间的随机数，然后加上最小值偏移
    return floorMin + Math.floor(Math.random() * range);
}
let _Effect = ["audios/click"];

export function playBtnEffect(event: any = null, customEventData: any = null) {
    if (!oops.audio.switchEffect) {
        return;
    }
    let index = 0;
    if (customEventData) index = Number(customEventData);
    oops.audio.playEffect(_Effect[index]);
}

export function formatSeconds(totalSeconds: number): string {
    // 计算各时间单位的值
    const days = Math.floor(totalSeconds / 86400); // 1天 = 86400秒
    const remainingSecondsAfterDays = totalSeconds % 86400;
    
    const hours = Math.floor(remainingSecondsAfterDays / 3600); // 1小时 = 3600秒
    const remainingSecondsAfterHours = remainingSecondsAfterDays % 3600;
    
    const minutes = Math.floor(remainingSecondsAfterHours / 60); // 1分钟 = 60秒
    const seconds = remainingSecondsAfterHours % 60;
    
    // 构建结果字符串
    const parts: string[] = [];
    if (days > 0) parts.push(`${Math.floor(days)}天`);
    if (hours > 0) parts.push(`${Math.floor(hours)}时`);
    if (minutes > 0) parts.push(`${Math.floor(minutes)}分`);
    if (seconds > 0) parts.push(`${Math.floor(seconds)}秒`);
    
    // 如果所有单位都为0，返回0秒
    return parts.length > 0 ? parts.join('') : '0秒';
}

// 长边总能显示完 b是容器
export function calculateScaledSize(aWidth: number, aHeight: number, bWidth: number, bHeight: number) {
    // 计算矩形a的宽高比
    const aRatio = aWidth / aHeight;
    // 计算矩形b的宽高比
    const bRatio = bWidth / bHeight;
    
    let scaledWidth, scaledHeight;
    
    // 比较两个矩形的宽高比来决定缩放基准
    if (aRatio <= bRatio) {
        // 以宽度为基准缩放，确保宽度充满b的宽度
        scaledWidth = bWidth;
        scaledHeight = bWidth / aRatio;
    } else {
        // 以高度为基准缩放，确保高度充满b的高度
        scaledHeight = bHeight;
        scaledWidth = bHeight * aRatio;
    }
    
    return {
        width: scaledWidth,
        height: scaledHeight
    };
}